# models.py
# Author: Nico Marrero
# Copyright 2012 Nico Marrero
#
#	 This file is part of django-serenity.
#
#    django-serenity is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    django-serenity is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with django-serenity.  If not, see <http://www.gnu.org/licenses/>.

from django.db import models
from django import forms
from django.contrib.auth.models import User, Group, Permission
from django.contrib.contenttypes.models import ContentType

from data import DICE_LEVELS, SKILLS, ASSET_COMPLICATION_LEVELS

import random
import re
import datetime

class SerenityAdventureLog(models.Model):
	date = models.DateField(default=datetime.datetime.today())
	title = models.CharField(max_length=512)
	entry = models.TextField()
	author = models.ForeignKey(User, blank=True, null=True, editable=False)

	class Meta:
		verbose_name = "Serenity Adventure Log"
		verbose_name_plural = "Serenity Adventure Log"
		permissions = (
			("change_all_serenityadventurelogs","Can change all logs"),
		)
	def initial_text(self):
		return re.sub('\<.*?\>', '', self.entry[:512])
	
class SerenityArmor(models.Model):
	name = models.CharField(max_length=255, unique=True)
	type = models.CharField(max_length=255)
	rating = models.CharField(max_length=255)
	covers = models.CharField(max_length=255)
	penalty = models.CharField(max_length=255)
	
	def __unicode__(self):
		return "%s (%s)" % (self.name, self.type)
	class Meta:
		verbose_name = "Serenity Armor"
		verbose_name_plural = "Serenity Armor"
	
class SerenityWeapon(models.Model):
	name = models.CharField(max_length=255, unique=True)
	type = models.CharField(max_length=255)
	damage = models.CharField(max_length=255)
	range = models.CharField(max_length=255)
	rate = models.CharField(max_length=255)
	ammo = models.CharField(max_length=255)
	
	def __unicode__(self):
		return "%s (%s)" % (self.name, self.type)
	class Meta:
		verbose_name = "Serenity Weapon"
		verbose_name_plural = "Serenity Weapons"
	
class SerenityCharacterSkillSpecialty(models.Model):
	character = models.ForeignKey('SerenityCharacter',related_name="specialties")
	skill = models.CharField(max_length=25,choices=SKILLS)
	specialty = models.CharField(max_length=255, help_text="Keep it as short as possible to fit on the character sheet")
	level = models.PositiveIntegerField(default=8,choices=DICE_LEVELS[4:])
	
	def __unicode__(self):
		return "%s - %s - %s" % (self.character.name, self.get_skill_display(), self.specialty)
	class Meta:
		verbose_name = "Serenity Character Skill Specialty"
		verbose_name_plural = "Serenity Character Skill Specialties"

class SerenityAssetComplication(models.Model):
	name = models.CharField(max_length=255)
	description = models.TextField()
	type = models.CharField(max_length=255,choices=(('asset','Asset'),('complication','Complication')))
	level = models.IntegerField(choices=ASSET_COMPLICATION_LEVELS)
	requires_specialization = models.BooleanField(default=False)
	original = models.BooleanField(default=True)
	double_cost = models.BooleanField(default=False)
	enabled = models.BooleanField(default=True)

	def short_name(self):
		if self.double_cost:
			return "%s (double %s)" % (self.name, self.get_level_display())
		else:
			return "%s (%s)" % (self.name, self.get_level_display())
		
	def __unicode__(self):
		if self.double_cost:
			return "%s (double %s) - %s" % (self.name, self.get_level_display(), self.description)
		else:
			return "%s (%s) - %s" % (self.name, self.get_level_display(), self.description)
		
	class Meta:
		verbose_name = "Serenity Asset/Complication"
		verbose_name_plural = "Serenity Assets/Complications"
		ordering = ['type', 'name', 'level']
		
		
class SerenityCharacter(models.Model):
	name = models.CharField(max_length=255, help_text="Character name (both first and last)")
	nickname = models.CharField(max_length=255, blank=True, null=True, help_text="(Optional) Character's nickname")
	player = models.ForeignKey(User, blank=True, null=True, related_name="characters")
	approved_by = models.ForeignKey(User, blank=True, null=True, related_name="approved_characters")
	home_world = models.CharField(max_length=255, blank=True, null=True, help_text="(Optional) Character's homeworld")
	concept = models.CharField(max_length=255, blank=True, null=True, help_text="(Optional) Character's concept")
	
	strength = models.PositiveIntegerField(default=4,choices=DICE_LEVELS[2:])
	agility = models.PositiveIntegerField(default=4,choices=DICE_LEVELS[2:])
	vitality = models.PositiveIntegerField(default=4,choices=DICE_LEVELS[2:])
	alertness = models.PositiveIntegerField(default=4,choices=DICE_LEVELS[2:])
	intelligence = models.PositiveIntegerField(default=4,choices=DICE_LEVELS[2:])
	willpower = models.PositiveIntegerField(default=4,choices=DICE_LEVELS[2:])
	
	animal_handling = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	artistry = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	athletics = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	covert = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	craft = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	discipline = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	guns = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	heavy_weapons = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	influence = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	knowledge = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	mechanical_engineering = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	linguist = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])

	medical_expertise = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	melee_weapons = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	perception = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	performance = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	pilot = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	planetary_vehicles = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	ranged_weapons = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	scientific_expertise = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	survival = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	technical_engineering = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	unarmed_combat = models.PositiveIntegerField(default=0,choices=DICE_LEVELS[:4])
	
	plot_points = models.PositiveIntegerField(default=0)
	character_gen_points = models.PositiveIntegerField(default=0)
	unused_advancement_points = models.PositiveIntegerField(default=0)
	total_advancement_points = models.PositiveIntegerField(default=0)
	
	credits = models.PositiveIntegerField(default=0, help_text="1cr = 2.5 plat = 5 gold = 250 silver")
	platinum = models.PositiveIntegerField(default=0)
	gold = models.PositiveIntegerField(default=0)
	silver = models.PositiveIntegerField(default=0)
	
	stuns_taken = models.PositiveIntegerField(default=0)
	wounds_taken = models.PositiveIntegerField(default=0)
	
	equipment = models.TextField(blank=True, null=True)
	
	history = models.TextField(blank=True, null=True)
	
	description = models.TextField(blank=True, null=True)
	
	notes = models.TextField(blank=True, null=True)
	
	class Meta:
		verbose_name = "Serenity Character"
		verbose_name_plural = "Serenity Characters"
		permissions = (
			("approve_serenitycharacter","Can approve characters"),
			("change_all_serenitycharacters","Can change all characters"),
		)
	def total_in_credits(self):
		return self.total_in_silver() * 0.004
	def total_in_platinum(self):
		return self.total_in_silver() * 0.01
	def total_in_gold(self):
		return self.total_in_silver() * 0.02
	def total_in_silver(self):
		return float(self.credits)*250.0 + float(self.platinum)*100.0 + float(self.gold)*50.0 + float(self.silver)
	def __unicode__(self):
		if not self.nickname:
			return "%s (%s %s)" % (self.name, self.player.first_name, self.player.last_name[0] if len(self.player.last_name) > 0 else self.player.last_name)
		else:
			return "'%s' %s (%s %s)" % (self.nickname, self.name, self.player.first_name, self.player.last_name[0] if len(self.player.last_name) > 0 else self.player.last_name)
	
	def end_of_session(self):
		if self.plot_points > 6:
			self.unused_advancement_points += self.plot_points - 6
			self.plot_points = 6
	@property
	def life_points(self):
		return self.vitality + self.willpower
	@property
	def endurance(self):
		return self.vitality + self.willpower
	@property
	def initiative(self):
		return self.agility + self.alertness
	@property
	def resistance(self):
		return 2*self.vitality

	@property
	def passed_out(self):
		if self.stuns_taken + self.wounds_taken >= 18:
			return True
		else: return False

	@property
	def dead(self):
		if self.wounds_taken >= self.life_points:
			return True
		else: return False
	
	@property
	def knocked_senseless(self):
		if self.stuns_taken >= self.life_points:
			return True
		else: return False
		
	def alive(self):
		return not self.dead
	alive.boolean = True
	
	def conscious(self):
		return not (self.knocked_senseless or self.passed_out or self.dead)
	conscious.boolean = True
	
	def approved(self):
		return (self.approved_by is not None)
	approved.boolean = True
	
class SerenityCharacterForm(forms.ModelForm):
	class Meta:
		model = SerenityCharacter
	def __init__(self, data = None, *args, **kwargs):
		super(SerenityCharacterForm, self).__init__(data, *args, **kwargs)
		if self.instance and data:
			CharacterDict = forms.model_to_dict(self.instance)
			data = self.data.copy()
			for field in CharacterDict:
				if not field in data:
					data[field] = CharacterDict[field]
			self.data = data
		
	def clean(self, *args, **kwargs):
		cleaned_data = super(SerenityCharacterForm, self).clean(*args, **kwargs)
		if self.data.has_key('wounds'):
			cleaned_data['wounds_taken'] = int(self.data['wounds'])
		if self.data.has_key('stuns'):
			cleaned_data['stuns_taken'] = int(self.data['stuns'])
		
		return cleaned_data
		
class SerenityCharacterArmor(models.Model):
	character = models.ForeignKey(SerenityCharacter,related_name="armor")
	armor = models.ForeignKey(SerenityArmor, blank=True, null=True)
	armor_notes = models.TextField(blank=True, null=True)
	
	def __unicode__(self):
		return "%s's Armor" % self.character.name
	
	class Meta:
		verbose_name = "Serenity Character Armor"
		verbose_name_plural = "Serenity Character Armor"
		
class SerenityCharacterWeapon(models.Model):
	character = models.ForeignKey(SerenityCharacter,related_name="weapons")
	weapon1 = models.ForeignKey(SerenityWeapon,related_name='+', blank=True, null=True)
	weapon1_ammo = models.PositiveIntegerField(default=0)
	weapon1_notes = models.TextField(blank=True, null=True)
	weapon2 = models.ForeignKey(SerenityWeapon,related_name='+', blank=True, null=True)
	weapon2_ammo = models.PositiveIntegerField(default=0)
	weapon2_notes = models.TextField(blank=True, null=True)
	weapon3 = models.ForeignKey(SerenityWeapon,related_name='+', blank=True, null=True)
	weapon3_ammo = models.PositiveIntegerField(default=0)
	weapon3_notes = models.TextField(blank=True, null=True)
	
	def __unicode__(self):
		return "%s's Weapons" % self.character.name
	class Meta:
		verbose_name = "Serenity Character Weapons"
		verbose_name_plural = "Serenity Character Weapons"

class SerenityCharacterAsset(models.Model):
	character = models.ForeignKey(SerenityCharacter,related_name="assets")
	asset = models.ForeignKey(SerenityAssetComplication, related_name="+")
	specialization = models.CharField(max_length=512,blank=True,null=True)
	class Meta:
		verbose_name = "Serenity Character's Asset"
		verbose_name_plural = "Serenity Characters' Assets"
		unique_together = ['character', 'asset']

	def __unicode__(self):
		str = "%s - %s" % (self.character, self.asset,)
		if self.specialization:
			str += " (%s)" % self.specialization
		return str
	def asset_name(self):
		return self.asset.short_name()
	def asset_effect(self):
		return self.asset.description
	
class SerenityCharacterComplication(models.Model):
	character = models.ForeignKey(SerenityCharacter,related_name="complications")
	complication = models.ForeignKey(SerenityAssetComplication, related_name="+")
	specialization = models.CharField(max_length=512,blank=True,null=True)
	class Meta:
		verbose_name = "Serenity Character's Complication"
		verbose_name_plural = "Serenity Characters' Complications"
		unique_together = ['character', 'complication']

	def __unicode__(self):
		str = "%s - %s" % (self.character, self.complication,)
		if self.specialization:
			str += " (%s)" % self.specialization
		return str

	def complication_name(self):
		return self.complication.short_name()
	def complication_effect(self):
		return self.complication.description
	
class SerenityCharacterAssetForm(forms.ModelForm):
	def __init__(self,*args,**kwargs):
		super(SerenityCharacterAssetForm,self ).__init__(*args,**kwargs)
		self.fields['asset'].queryset = SerenityAssetComplication.objects.filter(type='asset')
	def clean_specialization(self):
		if self.cleaned_data['asset'].requires_specialization and not self.cleaned_data['specialization']:
			raise forms.ValidationError("Asset %s requires specialization" % (self.cleaned_data['asset']))
		return self.cleaned_data["specialization"]
	class Meta:
		model = SerenityCharacterAsset
class SerenityCharacterComplicationForm(forms.ModelForm):
	def __init__(self,*args,**kwargs):
		super(SerenityCharacterComplicationForm,self ).__init__(*args,**kwargs)
		self.fields['complication'].queryset = SerenityAssetComplication.objects.filter(type='complication')
	def clean_specialization(self):
		if self.cleaned_data['complication'].requires_specialization and not self.cleaned_data['specialization']:
			raise forms.ValidationError("Complication %s requires specialization" % (self.cleaned_data['complication']))
		return self.cleaned_data["specialization"]
	class Meta:
		model = SerenityCharacterComplication

class SerenityDiceRoller(models.Model):
	title = models.CharField(max_length=512, blank=True, null=True)
	d2 = models.IntegerField(default=0)
	d4 = models.IntegerField(default=0)
	d6 = models.IntegerField(default=0)
	d8 = models.IntegerField(default=0)
	d10 = models.IntegerField(default=0)
	d12 = models.IntegerField(default=0)
	d2_results = models.CharField(max_length=512, default="", editable=False)
	d4_results = models.CharField(max_length=512, default="", editable=False)
	d6_results = models.CharField(max_length=512, default="", editable=False)
	d8_results = models.CharField(max_length=512, default="", editable=False)
	d10_results = models.CharField(max_length=512, default="", editable=False)
	d12_results = models.CharField(max_length=512, default="", editable=False)
	result = models.IntegerField(default=0,editable=False)
	created = models.DateTimeField(auto_now_add=True,editable=False)
	player = models.ForeignKey(User, blank=True, null=True)
	
	class Meta:
		verbose_name = "Dice Rolls"
		verbose_name_plural = "Dice Rolls"
		ordering = ['-created']
	
	def __unicode__(self):
		if self.title: return self.title
		ret_str = ""
		if self.d2: ret_str += "%s%sd2" % (' + ' if ret_str else '', self.d2)
		if self.d4: ret_str += "%s%sd4" % (' + ' if ret_str else '', self.d4)
		if self.d6: ret_str += "%s%sd6" % (' + ' if ret_str else '', self.d6)
		if self.d8: ret_str += "%s%sd8" % (' + ' if ret_str else '', self.d8)
		if self.d10: ret_str += "%s%sd10" % (' + ' if ret_str else '', self.d10)
		if self.d12: ret_str += "%s%sd12" % (' + ' if ret_str else '', self.d12)
		return ret_str if ret_str else "None"
	__unicode__.short_description = "Dice Roll"
	
	def __results__(self):
		ret_str = ""
		if self.d2: ret_str += "%s%sd2(%s)" % ('+' if ret_str else '', self.d2, self.d2_results)
		if self.d4: ret_str += "%s%sd4(%s)" % ('+' if ret_str else '', self.d4, self.d4_results)
		if self.d6: ret_str += "%s%sd6(%s)" % ('+' if ret_str else '', self.d6, self.d6_results)
		if self.d8: ret_str += "%s%sd8(%s)" % ('+' if ret_str else '', self.d8, self.d8_results)
		if self.d10: ret_str += "%s%sd10(%s)" % ('+' if ret_str else '', self.d10, self.d10_results)
		if self.d12: ret_str += "%s%sd12(%s)" % ('+' if ret_str else '', self.d12, self.d12_results)
		return ret_str
	__results__.short_description = "Dice Results"
	
	def clone(self):
		NewRoll = SerenityDiceRoller(title=self.title,d2=self.d2,d4=self.d4,d6=self.d6,d8=self.d8,d10=self.d10,d12=self.d12,result=self.result,
									 d2_results=self.d2_results,d4_results=self.d4_results,d6_results=self.d6_results,d8_results=self.d8_results,
									 d10_results=self.d10_results,d12_results=self.d12_results)
		if self.title:
			TitleRE = re.search('(.*?)(\d+)$', self.title)
			NewTitle = TitleRE.group(1)
			RepeatNumber = TitleRE.group(2)
			if RepeatNumber:
				NewRoll.title = "%s%s" % (TitleRE.group(1),int(RepeatNumber)+1)
		return NewRoll
	
	def roll(self):
		self.result = 0
		self.d2_results = self.d4_results = self.d6_results = self.d8_results = self.d10_results = self.d12_results = ""
		if self.d2:
			for i in range(self.d2): res = random.randint(0,1); self.result += res; self.d2_results += "%s%s" % (',' if self.d2_results else '', res)
		if self.d4:
			for i in range(self.d4): res = random.randint(0,3); self.result += res; self.d4_results += "%s%s" % (',' if self.d4_results else '', res)
		if self.d6:
			for i in range(self.d6): res = random.randint(0,5); self.result += res; self.d6_results += "%s%s" % (',' if self.d6_results else '', res)
		if self.d8:
			for i in range(self.d8): res = random.randint(0,7); self.result += res; self.d8_results += "%s%s" % (',' if self.d8_results else '', res)
		if self.d10:
			for i in range(self.d10): res = random.randint(0,9); self.result += res; self.d10_results += "%s%s" % (',' if self.d10_results else '', res)
		if self.d12:
			for i in range(self.d12): res = random.randint(0,11); self.result += res; self.d12_results += "%s%s" % (',' if self.d12_results else '', res)
		self.save()
		return self.result
